#  create Proxy with simple error behaviour :
# on error select another ip::port from pool and try to reconects up to $REPEAT times
package Net::Coro::ProxyBuilder;
use common::sense;
our $VERSION = 0.02;

use Net::Coro::ProxyPool;

our $REPEAT = 5;
our $DEBUG = 0;
our @ISA;

sub new {
	my ($class, $base, $pool, $socket) =@_;
	@ISA = ($base);
	
	$pool = Net::Coro::ProxyPool->new ($pool) if !(ref $pool);
	my ($host, $port) = $pool->get;
	my $this = $class->SUPER::new($host, $port,$socket);
	
	$this->{_POOL} = $pool;
	$this->{_REPEAT_LIMIT} = $REPEAT;
	$this->{_REPEAT} = $this->{_REPEAT_LIMIT};
	$this->id(rand()) if $DEBUG;

	return $this;
}

sub child {
	my ($this) = @_;
	my $child = $this->SUPER::child();
	$child->{_PARRENT_SOCK} = 1;
	return $child;
}

sub callback {
	my ($this, @resp) = @_;
	$this->{_REPEAT} = $this->{_REPEAT_LIMIT};
	
	$this->SUPER::callback(@resp);
}

sub release_sock {
	my ($this) = @_;
	if (!$this->{_PARRENT_SOCK}) {
		$this->{_POOL}->free(
			delete ($this->{_SCKHOST}),
			delete ($this->{_SCKPORT})
		);
	} else {
		delete $this->{_PARRENT_SOCK};
	}
}

sub err {
	my ($this, $msg, $code) = @_;
	
	# retry
	if ($this->{_REPEAT}--) {
		$this->close;

		print "PROXY $this->{_ID}: proxy $this->{_SCKHOST}:$this->{_SCKPORT} fail. try left $this->{_REPEAT}. ($msg, $code)\n" if $DEBUG;
		$this->release_sock;
		($this->{_SCKHOST}, $this->{_SCKPORT}) = $this->{_POOL}->get;
		$this->connect($this->{_HOST}, $this->{_PORT}, $this->{_CALLER});
		return;
	}
	
	# send error
	print "PROXY $this->{_ID}: proxy $this->{_SCKHOST}:$this->{_SCKPORT} fail. no more attempts. give up. \n" if $DEBUG;
	$this->{_REPEAT} = $this->{_REPEAT_LIMIT};
	$this->SUPER::err($msg, $code);
}

sub stop {
	my ($this) = @_;
	
	$this->release_sock;		
	$this->SUPER::stop;
}

sub repeat {
	my ($this, $val) = @_;
	$this->{_REPEAT_LIMIT} = $val if defined $val;
	return $this->{_REPEAT_LIMIT};
}


# pool interface
sub get {
	my ($this) = @_;
	return ($this->{_SCKHOST}, $this->{_SCKPORT});
}
sub free {}

######## static methods #####
# return two related Proxy object for ftp command and data channels
sub ftp {
	my ($class, $base, $file, $socket) = @_;
	my $cmd_sck = Net::Coro::ProxyBuilder->new($base, $file, $socket);
	my $data_sck = Net::Coro::ProxyBuilder::FTPData->new($base, $cmd_sck, $socket);
	$data_sck->repeat(2);
	return ($cmd_sck, $data_sck);
}

# in common case the ftp data channel should use the same Proxy as command channel.
# that is why we should get actual proxy address every time we execute connect method.
package Net::Coro::ProxyBuilder::FTPData;
use common::sense;
use base 'Net::Coro::ProxyBuilder';
use constant {
	_SCKHOST => 12,
	_SCKPORT => 13,
	_POOL => 20,
};
sub connect {
	my ($this, @prm) = @_;
	
	# release proxy if any
	if (defined $this->{_SCKHOST}) {
		$this->{_POOL}->free(
			delete ($this->{_SCKHOST}),
			delete ($this->{_SCKPORT})
		);
	}
	# get current command channel proxy
	($this->{_SCKHOST}, $this->{_SCKPORT}) = $this->{_POOL}->get;
	
	$this->SUPER::connect(@prm);
}
1;